﻿
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xaml;
using System.Xaml.Schema;

using alib;
using alib.Debugging;

namespace agree.schema.xaml
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class xaml_schema : XamlSchemaContext
	{
		public xaml_schema(Schema sch)
		{
			this.sch = sch;
		}

		readonly Schema sch;

		protected override Assembly OnAssemblyResolve(String s_asm)
		{
			Assembly a;
			if ((a = base.OnAssemblyResolve(s_asm)) == null)
			{
				String s_found = SysObj.Instance.DefaultDirectories.FindBinaryFile(s_asm + ".dll", null);
				if (s_found == null)
					throw new ConfigException("The assembly name '{0}' could not be resolved", s_asm);

				s_found = Path.GetFullPath(s_found);

				a = Assembly.LoadFile(Path.GetFullPath(s_found));

				if (a == null)
					throw new ConfigException("The assembly {0} could not be loaded", s_found);
			}
			return a;
		}

		/// <summary> streamlined resolution of CLR types with support for generic types </summary>
		public Type FindType(XamlTypeName name)
		{
			Type t;
			String s_ns, s_asm;

			if (!name.Namespace.TryParseUri(out s_ns, out s_asm))
			{
				t = GetXamlType(name).UnderlyingType;
			}
			else
			{
				var a = OnAssemblyResolve(s_asm);

				int cg = name.TypeArguments.Count;

				var s_name = make_systype_name(s_ns, name.Name, cg);

				if ((t = a.GetType(s_name)) == null)
				{
					var a_core = Schema.rg_astats[0].asm;
					if (a != a_core && (t = a_core.GetType(s_name)) == null)
					{
						s_name = make_systype_name(s_ns, name.Name, cg, "composition");

						if ((t = a_core.GetType(s_name)) == null)
						{
							String msg = String.Format("Could not find type '{0}'", name.ToString());
							throw new Exception(msg);
						}
					}
				}

				if (cg > 0)
				{
					var rgT_open = t.GetGenericArguments();
					Debug.Assert(rgT_open.Length == name.TypeArguments.Count);

					Type[] rgT_close = new Type[cg];
					for (int i = 0; i < cg; i++)
						rgT_close[i] = FindType(name.TypeArguments[i]).UnderlyingSystemType;

					t = t.MakeGenericType(rgT_close);
				}
			}
			return sch.promote_type(t);
		}

		String make_systype_name(String s_ns1, String name, int cg, String s_ns2 = null)
		{
			String s_name = s_ns1 + ".";
			if (s_ns2 != null)
				s_name += s_ns2 + ".";
			s_name += name;
			if (cg > 0)
				s_name += "`" + cg;
			return s_name;
		}

		protected override XamlType GetXamlType(String s_uri, String name, params XamlType[] rgT_generic_args)
		{
			XamlType xt;

			if (rgT_generic_args != null)
			{
				var xtn = new XamlTypeName(s_uri, name, rgT_generic_args.Select(z => new XamlTypeName(z)));
				xt = this.GetXamlType(FindType(xtn));
			}
			else
			{
				if (name == "P")
					s_uri = "clr-namespace:agree.schema;assembly=agree-sys";
				else if (name == "PP")
					s_uri = "clr-namespace:agree.schema;assembly=agree-core";

				xt = base.GetXamlType(s_uri, name, null);
			}

			if (xt != null)
				return xt;

			if (name.EndsWith("Extension"))
				return this.GetXamlType(s_uri, name.Substring(0, name.Length - 9), rgT_generic_args);

			if (s_uri != Schema.s_core_asm)
				return this.GetXamlType(Schema.s_core_asm, name, rgT_generic_args);

			var fullname = "agree.composition." + name;
			var ix = sch.type_names[fullname];
			if (ix != -1)
				return sch.type_by_ix[ix].XamlType;

			return null;
		}

		public sealed override XamlType GetXamlType(Type t)
		{
			schema_type st;
			return (st = sch.promote_type(t) as schema_type) != null ? st.XamlType : base.GetXamlType(t);
		}
	};
}
